Skip to content

chore: sync upstream 2026-09-13 - #77

Merged
arrrrny merged 2 commits into
masterfrom
sync/fork-sync-resolution-2026-09-13
Sep 13, 2026
Merged

chore: sync upstream 2026-09-13#77
arrrrny merged 2 commits into
masterfrom
sync/fork-sync-resolution-2026-09-13

Conversation

@arrrrny

@arrrrny arrrrny commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Related Issue

Resolve #75

(Issue #76 is the catch-all failure issue opened by the same sync run — the only failing step was the conflict abort below, so it has no separate cause.)

Problem

The daily upstream sync could not auto-merge upstream ee2cac102 ("invalidate streamed attempt state when the llm requester retries below the turn") into fork master. Three fork-owned files conflicted:

  • packages/agent-core-v2/src/agent/llmRequester/llmRequester.ts
  • packages/agent-core-v2/src/agent/llmRequester/llmRequesterService.ts
  • packages/agent-core-v2/test/agent/llmRequester/llmRequesterService.test.ts

Both sides had diverged in the same interfaces: upstream added an onAttemptRetry callback for discarding an interrupted attempt's streamed state, while the fork carries the model request override and the terminal-400 fallback cascade.

What changed

Merged upstream, resolving the three conflicts additively so no fork-owned code was dropped:

  • AgentLLMRequestOverrides now carries both the fork's model override and upstream's onAttemptRetry.
  • runRequest keeps the fork's internal resolveRequest(overrides) call (it must re-resolve after activateFallback('terminal-error')) and additionally threads upstream's onAttemptRetry.
  • The fork's terminal-400 fallback path now also calls onAttemptRetry, so the interrupted attempt's streamed state is discarded when the fallback model takes over — the same below-turn retry case upstream's fix targets. Without this, a partial tool call streamed before the 400 could leak into the fallback attempt's message.
  • The test file keeps both suites: the fork's terminal-error fallback tests (with a new assertion that onAttemptRetry fires on fallback) and upstream's attempt-retry / turn-machine stream-state tests.

Verification

  • vitest run test/agent/llmRequester/llmRequesterService.test.ts — 46/46 pass (fork fallback and upstream attempt-retry suites together).
  • vitest run test/agent/loop (including upstream's new machineTools.test.ts) — passes except loop.test.ts, which fails identically on pristine master in the same environment.
  • Full agent-core-v2 suite compared against a pristine master worktree: identical genuine failures (pre-existing/environmental). The 8 files that differed on a first pass all passed on re-run.
  • .github/FORK_OWNED_FILES guardrail: 31 entries, 0 missing, 0 markers dropped — activateFallback, substituteModelActiveKey, and isTerminalProviderApiError all survive.

Follow-up (not changed here)

The fork-owned-file guardrail in .github/workflows/sync-upstream.yml cannot parse .github/FORK_OWNED_FILES's padded path :: marker format — its sed leaves trailing whitespace, so every entry reads as missing and the guardrail would fail the next clean sync. Worth fixing separately.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset. (No new changeset: upstream's .changeset/fix-infinite-retry-stream-invalidation.md already covers the user-facing effect.)
  • Ran gen-docs skill, or this PR needs no doc update. (Upstream's docs/{en,zh}/llm.md updates are included in the merge.)

@github-actions

Copy link
Copy Markdown

❌ Nix build failed

Hash mismatch in pnpmDeps:

Hash
specified sha256-rKG18o/SNptyamq3LKveIEN/1LA0myX0JK/o6+yQ5Js=
got sha256-4jpewFz0cw4nrv9dAwX3Q0VVgBC4HHZUxp+J8ARHrlQ=

Please update flake.nix with the got hash.

@arrrrny
arrrrny merged commit b327557 into master Sep 13, 2026
8 of 16 checks passed

@zuraffa-review zuraffa-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR review — chore: sync upstream 2026-09-13 @ 8394b6ee

Fork sync merging upstream MoonshotAI/kimi-code@ee2cac102 (fix(agent-core-v2): invalidate streamed attempt state when the llm requester retries below the turn, MoonshotAI#3734) into fork master, resolving the three conflicting llmRequester* files additively.

Note: this PR merged (as b3275579) while the review was in flight — the review is pinned to the reviewed commit 8394b6ee; findings below are informational and unblocked.

Merge audit — verified against the real upstream commit

Check Result
Upstream scope upstream ee2cac102 touches the same 12 files (llmRequester.ts, llmRequesterService.ts, loop/machine/requester.ts, loop/machine/tools.ts, human/agent/turn.ts, llm/requester/requester.ts, model-requester-impl.ts, both llm.md docs, .changeset file, 2 test files)
Upstream hunks preserved 9/12 files are byte-identical to upstream's post-commit state; the 3 diverging files are exactly the conflicts, and every divergence is fork-owned content (model override, substitute/fallback cascade, fallback tests) — no upstream change was dropped
Fork code preserved model?: string, substituteModelActiveKey, activateFallback + terminal-400 fallback all intact; the fallback path is wired into the new onAttemptRetry contract (llmRequesterService.ts:496) and asserted by the fork's own fallback test
Fork-owned-file guardrail re-ran it independently on the merge commit: 31 entries, 0 missing files, 0 missing markers
Regression coverage the new turn-level test reproduces "partial tool call streamed → request retried below the turn" and asserts only the surviving attempt's call id; machineTools.test.ts covers duplicate-id supersede + onBatchError

Findings

🔴 0 · 🟠 0 · 🟡 0 · 🔵 3 (all non-blocking, inline on the diff):

  1. 🔵 packages/agent-core-v2/src/agent/loop/machine/tools.ts:144startBatch's .catch is unreachable in practice.
  2. 🔵 packages/agent-core-v2/src/agent/llmRequester/llmRequesterService.ts:340 — redundant onAttemptRetry parameter in the merged runRequest shape.
  3. 🔵 packages/agent-core-v2/src/llm-adapter/model/model-requester-impl.ts:216-222 — the new llm.request.retrying reset has no producer at that layer today.

Out-of-diff note — the guardrail follow-up claim looks incorrect on CI

The PR body says the fork-owned-file guardrail "cannot parse .github/FORK_OWNED_FILES's padded path :: marker format — its sed leaves trailing whitespace, so every entry reads as missing and the guardrail would fail the next clean sync."

  • I reproduced the described failure on macOS: BSD sed does not implement \+ in a BRE, so sed -e 's/[[:space:]]\+::.*$//' does not match a padded line — the "path" keeps its padding and the ::marker tail, and [ -f "$path" ] fails. That is exactly the symptom in the note.
  • However the workflow runs on ubuntu-latest (.github/workflows/sync-upstream.yml:31), i.e. GNU sed, where \+ is the documented GNU BRE extension for "one or more": [[:space:]]\+ matches the whole run of spaces and the padded format parses correctly there.
  • So on CI the guardrail is not broken; the only real nit is portability if that step is ever run locally on macOS — [[:space:]][[:space:]]* (or [[:space:]]\{1,\}) makes it platform-proof. Worth a quick re-check on the runner before changing the workflow, so a working guardrail is not "fixed" into something different.

For the fixing agent: the three 🔵 inline items above plus this guardrail-diagnosis note are the complete findings list; all optional, nothing blocks.

};

const startBatch = (entries: readonly PendingEntry[]): void => {
void runBatch(entries).catch((error: unknown) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Nitpick (⚡) — startBatch's .catch is unreachable in practice: runBatch already reports through options.onBatchError in its own catch (tools.ts:132-137) and never rethrows, so the promise only rejects if onBatchError itself throws — and then the same error gets reported twice. If you want a single report path, drop this wrapper (or rethrow in runBatch's catch and report only here). It is upstream's line, so leaving it as-is is also a reasonable call.

onPart: AgentLLMRequestPartHandler,
signal: AbortSignal | undefined,
onRequestTrace: (traceId: string | undefined) => void,
onAttemptRetry: (() => void) | undefined,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Nitpick (⚡) — in the fork's merged shape this parameter duplicates overrides.onAttemptRetry: runRequest already receives overrides (line 336) and the only call site passes exactly overrides.onAttemptRetry (line 268). Upstream needed the separate parameter because its runRequest takes a resolved request; here const onAttemptRetry = overrides.onAttemptRetry; at the top of the body removes the parallel argument. Low priority — keeping it to stay textually close to upstream is defensible.

Comment on lines +216 to +222
case 'llm.request.retrying': {
accumulator = createMessageAccumulator();
usage = undefined;
finish = undefined;
messageId = undefined;
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Observation (⚡) — at this commit nothing emits llm.request.retrying at this layer: the loop-machine requester (agent/loop/machine/requester.ts:86) is the only emitter and it sits above ModelRequesterImpl, while the base protocol requesters (human/llm/requester/bases/*/requester.ts) emit llm.sent but never this event — so this reset currently cannot fire (each service-level retry re-enters request() with a fresh local accumulator anyway). Harmless and plausibly forward-looking; flagging so nobody debugs a stale streamed state expecting this branch to have run.

@zuraffa-review

Copy link
Copy Markdown

Walkthrough — chore: sync upstream 2026-09-13 @ 8394b6ee

What this PR does: merges upstream MoonshotAI/kimi-code@ee2cac102 (MoonshotAI#3734 — a below-the-turn LLM retry must invalidate the interrupted attempt's streamed state) and resolves the three conflicting llmRequester* files additively: the fork's model override and terminal-400 fallback cascade now sit alongside upstream's onAttemptRetry notification.

How it was reviewed:

  • Diffed the merge against the actual upstream commit: 9/12 files are byte-identical to upstream's post-commit content; the 3 that differ are exactly the conflicted files, and their deltas are fork-only (model?: string, substituteModelActiveKey, activateFallback, fallback tests) — every upstream hunk is retained, including onAttemptRetry at all three retry sites (projection resend, terminal-400 fallback, infinite-retry backoff).
  • Traced the new contract end-to-end: loop-machine requester emits llm.request.retrying on service-internal retries → turn's discardAttemptStream rolls back the accumulator + tool-call-id normalizer → ModelRequesterImpl resets its accumulator; machineTools dedupes expectedIds, guards missing pending entries, and supersedes duplicate-id calls — closing the crash path from the changeset.
  • Re-ran the fork-owned-file guardrail against the merge commit: 31 entries, 0 missing files, 0 missing markers.
  • Reviewed the new tests: the turn-level infinite-retry test is a genuine regression test for the streamed-state leak; machineTools.test.ts covers the duplicate-id batch and the executor-failure onBatchError path.

Outcome: 0 🔴 / 0 🟠 / 0 🟡 / 3 🔵 — the three nitpicks are posted inline and are optional cleanups; nothing blocks. One out-of-diff note: the PR body's guardrail-parsing follow-up looks incorrect for CI (the ubuntu runner's GNU sed handles the \+ one-or-more extension fine — the failure only reproduces under BSD sed); details in the review body.

Note: the PR merged as b3275579 while this review was in flight, so the review is informational and pinned to 8394b6ee.

@arrrrny

arrrrny commented Sep 13, 2026

Copy link
Copy Markdown
Owner Author

Thanks — the guardrail note is correct and my diagnosis was wrong.

Re-checking the committed bytes (git show b8074763e:.github/workflows/sync-upstream.yml | sed -n 158p | od -c), the original line was sed -e 's/[[:space:]]\+::.*$//' — GNU BRE \+. On ubuntu-latest that matches the whole padding run, so the guardrail parsed correctly. My "reproduction" used [[:space:]]:: because the \+ was lost when I transcribed the pattern from the rendered file — I reproduced a bug in a pattern CI never ran. git log --author=github-actions confirms prior clean syncs pushed, which requires the guardrail to have passed.

I've corrected PR #78's description: the change (POSIX [[:space:]]*::, marker trailing-trim, malformed-entry guard) stands as portability/local-verifiability, not a CI fix. The catch-all dedup half of #78 is unaffected and still valid.

On the three inline items:

  • tools.ts:144 and model-requester-impl.ts:216 — upstream-owned lines; leaving them untouched to stay textually close to upstream.
  • llmRequesterService.ts:340 — keeping the explicit onAttemptRetry parameter deliberately. The fork's runRequest takes overrides, so the parameter is formally redundant, but it keeps the signature tail aligned with upstream's, which is what kept this sync's conflict surface to a single parameter. Inlining it would trade one redundancy for a wider future conflict.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sync: upstream merge conflicts require manual resolution

2 participants